#pragma once

#include <sys/epoll.h>
#include "Commn.hpp"
#include "Log.hpp"
#include "Reactor.hpp"

using namespace LogModule;
class IoService : public Connection
{
private:
    int _fd;
    handler_t _handler;
public:
    IoService(int fd)
        : _fd(fd)
    {
        SetFd(fd);
        SetEvent(EPOLLIN | EPOLLOUT | EPOLLET);
        SetNonBlock(fd);
    }
    void RegisterHandler(handler_t handler) override { _handler = handler; }

    void Reader() override
    {
        char buffer[1024];
        while (true)
        {
            int n = ::recv(_fd, buffer, sizeof(buffer) - 1, 0);
            if (n > 0)
            {
                buffer[n] = 0;
                AppendReadBuffer(buffer);
            }
            else if (n == 0)
            {
                Exceptor();
            }
            else
            {
                if (errno == EAGAIN)
                {
                    LOG(LogLevel::INFO) << "all data read done";
                    break;
                }
                else if (errno == EINTR)
                {
                    LOG(LogLevel::INFO) << "read interrupted by signo, read again";
                    continue;
                }
                else
                {
                    Exceptor();
                    break;
                }
            }
        }
        // 直接处理数据
        std::string return_str = _handler(GetInBuffer());
        AppendOutBuffer(return_str);

        if (!IsOutBufferEmpty())
        {
            Sender(GetOuBuffer());
        }
    }

    void Sender(std::string &out) override
    {
        while (true)
        {
            int n = ::send(_fd, out.c_str(), out.size(), 0);
            if (n > 0)
            {
                DisCard(n);
            }
            else
            {
                 if (errno == EAGAIN)
                {
                    LOG(LogLevel::INFO) << "all data write done";
                    break;
                }
                else if (errno == EINTR)
                {
                    LOG(LogLevel::INFO) << "write interrupted by signo, read again";
                    continue;
                }
                else
                {
                    Exceptor();
                    break;
                }
            }
        }
    }

    void Exceptor() override
    {
        LOG(LogLevel::INFO) << "client quit: " << _fd;
        GetOwner()->DeletConnection(_fd);
    }
    ~IoService() {}
};